package com.rbac.web.controller;

import com.rbac.web.exception.ApplicationException;
import com.rbac.web.model.PageResult;
import com.rbac.web.model.Permission;
import com.rbac.web.model.PermissionTree;
import com.rbac.web.model.PermissionType;
import com.rbac.web.model.RolePermission;
import com.rbac.web.model.UserContext;
import com.rbac.web.model.req.PermissionAddReq;
import com.rbac.web.model.req.PermissionGetListReq;
import com.rbac.web.model.res.PermissionGetListRes;
import com.rbac.web.model.res.PermissionTreeGetRes;
import com.rbac.web.service.IPermissionService;
import com.rbac.web.service.IRolePermissionService;
import com.rbac.web.service.IUserRoleService;
import com.rbac.web.service.IUserTokenService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author gengaoliu
 */
@RestController
@RequestMapping("/permission")
@Tag(name = "权限管理", description = "刘澳港")
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class PermissionController {
  private static final Integer TOP = 0;
  private final IPermissionService permissionService;
  private final IUserTokenService tokenService;
  private final IUserRoleService userRoleService;
  private final IRolePermissionService rolePermissionService;

  @PostMapping
  @Operation(summary = "添加菜单")
  public void savePermission(@RequestBody @Validated PermissionAddReq req) {
    permissionService.insert(req.toPermission());
  }

  @PutMapping
  @Operation(summary = "修改菜单")
  public void update(@RequestBody @Validated PermissionAddReq req) {
    permissionService.updateByEntity(req.toPermission());
  }

  @GetMapping
  @Operation(summary = "菜单列表")
  public PageResult<PermissionGetListRes> getList(@ParameterObject PermissionGetListReq req) {
    return req.execute(req.toPageV0(), permissionService);
  }

  @DeleteMapping("/{id}")
  @Operation(summary = "删除菜单")
  public void delete(@PathVariable Integer id) {
    permissionService.deleteById(id);
  }

  @GetMapping("/tree")
  @Operation(summary = "菜单树形结构")
  public List<PermissionTreeGetRes> getTree() {
    List<Permission> all = permissionService.findAll();
    List<Permission> buttonList =
        all.stream().filter(x -> PermissionType.BUTTON.equals(x.getType())).toList();
    List<Permission> menuList =
        all.stream().filter(x -> PermissionType.MENU.equals(x.getType())).toList();
    return menuList.stream()
        .filter(x -> TOP.equals(x.getPId()))
        .map(
            x ->
                PermissionTreeGetRes.builder()
                    .id(x.getId())
                    .displayName(x.getDisplayName())
                    .buttonList(getButtonList(buttonList, x.getId()))
                    .childrenList(getChildrenList(x.getId(), menuList, buttonList))
                    .build())
        .toList();
  }

  @GetMapping("/menu")
  @Operation(summary = "获取当前登陆账号的所有菜单")
  public List<PermissionTree> getMenus() {
    Integer userId = UserContext.get().getId();
    AtomicReference<Integer> roleId = new AtomicReference<>(0);
    if (Boolean.TRUE.equals(tokenService.haveUserRole(userId))) {
      roleId.set(tokenService.getUserRole(userId));
    } else {
      userRoleService.findByUserId(userId).ifPresent(userRole -> roleId.set(userRole.getRoleId()));
    }
    if (TOP.equals(roleId.get())) {
      throw ApplicationException.createException("该账号未设置权限，请联系管理员");
    }
    List<Integer> permissionIds =
        rolePermissionService.findByRoleId(roleId.get()).stream()
            .map(RolePermission::getPermissionId)
            .toList();
    if (!permissionIds.isEmpty()) {
      List<Permission> permissionList = permissionService.findAllByIds(permissionIds);
      List<PermissionTree> permissionTreeList =
          permissionList.stream()
              .filter(
                  x ->
                      PermissionType.MENU.equals(x.getType())
                          || PermissionType.BUTTON.equals(x.getType()))
              .map(Permission::toPermissionTree)
              .sorted(
                  Comparator.comparing(
                      PermissionTree::getSort, Comparator.nullsLast(Integer::compareTo)))
              .toList();
      return buildTreeByUser(permissionTreeList);
    }
    return Collections.emptyList();
  }

  private List<PermissionTree> buildTreeByUser(List<PermissionTree> permissionTreeList) {
    List<PermissionTree> root = new ArrayList<>();
    List<PermissionTree> allButtons =
        permissionTreeList.stream().filter(x -> PermissionType.BUTTON.equals(x.getType())).toList();
    List<PermissionTree> allMenus =
        permissionTreeList.stream().filter(x -> PermissionType.MENU.equals(x.getType())).toList();
    Map<Integer, List<PermissionTree>> parentMap = parentMap(root, allMenus);
    for (PermissionTree menu : allMenus) {
      List<PermissionTree> buttons =
          allButtons.stream().filter(button -> button.getMenuId().equals(menu.getId())).toList();
      menu.setButtonList(buttons);
      menu.setChildren(parentMap.get(menu.getId()));
    }
    root.stream()
        .sorted(
            Comparator.comparing(PermissionTree::getSort, Comparator.nullsLast(Integer::compareTo)))
        .forEach(r -> r.setChildren(parentMap.get(r.getId())));
    return root;
  }

  private Map<Integer, List<PermissionTree>> parentMap(
      List<PermissionTree> root, List<PermissionTree> allMenus) {
    // 找出根结点
    for (PermissionTree next : allMenus) {
      if (next.isTop(TOP)) {
        root.add(next);
      }
    }
    return allMenus.stream()
        .filter(m -> Optional.ofNullable(m.getPId()).isPresent())
        .sorted(
            Comparator.comparing(PermissionTree::getSort, Comparator.nullsLast(Integer::compareTo)))
        .collect(Collectors.groupingBy(PermissionTree::getPId));
  }

  private List<PermissionTreeGetRes> getChildrenList(
      Integer pId, List<Permission> menuList, List<Permission> buttonList) {
    return menuList.stream()
        .filter(x -> x.getPId().equals(pId))
        .map(
            x ->
                PermissionTreeGetRes.builder()
                    .id(x.getId())
                    .displayName(x.getDisplayName())
                    .buttonList(getButtonList(buttonList, x.getId()))
                    .childrenList(getChildrenList(x.getId(), menuList, buttonList))
                    .build())
        .toList();
  }

  private List<PermissionTreeGetRes> getButtonList(List<Permission> buttonList, Integer id) {
    return buttonList.stream()
        .filter(x -> x.getMenuId().equals(id))
        .map(
            x ->
                PermissionTreeGetRes.builder()
                    .id(x.getId())
                    .displayName(x.getDisplayName())
                    .build())
        .toList();
  }
}
